package com.wjj.sixinone.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wjj.sixinone.common.enums.ExceptionEnum;
import com.wjj.sixinone.common.enums.RedisConstantEnum;
import com.wjj.sixinone.common.exception.GlobalException;
import com.wjj.sixinone.common.pojo.entity.BanIp;
import com.wjj.sixinone.common.pojo.entity.User;
import com.wjj.sixinone.common.util.AuthContextUtil;
import com.wjj.sixinone.common.util.CurrentIpUtils;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class IpAddressInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    // 存储每个IP和访问时间
    private static final Map<String, Long> ipAccessMap = new ConcurrentHashMap<>();
    // 存储每个IP和访问次数
    private static final Map<String, Integer> ipAccessMapNum = new ConcurrentHashMap<>();
    private static final long WINDOW_TIME_MILLIS = 60 * 1000; // 1分钟
    private static final int MAX_REQUESTS_PER_WINDOW = 500; // 1分钟内最多请求次数
    private static final long BLOCK_TIME_MILLIS = 5 * 60 * 1000; // 封禁时间

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ip = CurrentIpUtils.getUserIP();

        // 检查Redis中是否存在该IP的封禁记录
        String banIpJson = redisTemplate.opsForValue().get(RedisConstantEnum.BAN_IP.getKey() + ip);
        if (banIpJson != null) {
            BanIp banIp = objectMapper.readValue(banIpJson, BanIp.class);
            if (banIp.getBanTime().plusMinutes(BLOCK_TIME_MILLIS / (60 * 1000)).isAfter(LocalDateTime.now())) {
                throw new GlobalException(ExceptionEnum.ALREDY_BAN_IP);
            }
        }

        ipAccessMap.putIfAbsent(ip, System.currentTimeMillis());
        ipAccessMapNum.putIfAbsent(ip, 0);
        long currentTime = System.currentTimeMillis();

        if (currentTime - ipAccessMap.get(ip) > WINDOW_TIME_MILLIS) {
            ipAccessMap.put(ip, currentTime);
            ipAccessMapNum.put(ip, 0); // 重置计数器
        }

        // 检查IP是否在窗口内的最大访问次数上限
        if (ipAccessMapNum.get(ip) > MAX_REQUESTS_PER_WINDOW) {
            // 封禁 IP，存储到 Redis，并重置计数器
            BanIp banIp = new BanIp();
            banIp.setIp(ip);
            banIp.setBanTime(LocalDateTime.now());
            banIp.setBanReason("请求超限");
            User user = AuthContextUtil.get();
            if (user != null) {
                banIp.setBanUser(user.getUsername());
            }
            banIp.setUserIp(ip);

            String newBanIpJson = objectMapper.writeValueAsString(banIp);
            redisTemplate.opsForValue().set(RedisConstantEnum.BAN_IP.getKey() + ip, newBanIpJson);

            throw new GlobalException(ExceptionEnum.ERROR_REQUEST);
        }

        // 增加IP的访问计数
        ipAccessMapNum.put(ip, ipAccessMapNum.get(ip) + 1);
        return true;
    }
}
